/*********** SERVIDOR NÍVEL 1 ************/
/* Encoding: UTF8 */

#include <sys/types.h>   // Definicao de tipos
#include <sys/socket.h>  // Biblioteca de estrutara de sockets
#include <netinet/in.h>  // Define os padroes de protocolo IP
#include <arpa/inet.h>   // Converte enderecos hosts
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // Define constantes POSIX
#include <errno.h>  // Utilizado para capturar mensagens de erro
#include <sys/time.h>

void concatena(int a[],int b[],int c[]){   
  int i;
   
  for (i=0;i<1250;i++)
  {
    c[i] = a[i];
    c[i+1250] = b[i] ; 
  }
}

void quickSort( int a[], int l, int r)
{
   int j;

   if( l < r ) 
   {
    // divide and conquer
        j = partition( a, l, r);
       quickSort( a, l, j-1);
       quickSort( a, j+1, r);
   } 
}

int partition( int a[], int l, int r) {
   int pivot, i, j, t;
   pivot = a[l];
   i = l; j = r+1;
    
   while( 1)
   {
    do ++i; while( a[i] <= pivot && i <= r );
    do --j; while( a[j] > pivot );
    if( i >= j ) break;
    t = a[i]; a[i] = a[j]; a[j] = t;
   }
   t = a[l]; a[l] = a[j]; a[j] = t;
   return j;
}

int main(int argc, char *argv[])
{   
  // Variáveis
  int recsock, sendsock, connected[2], i, true = 1, connections = 0;
  int vectorin[2][1250];
  int vectorout[2500];
  int sortedvector[10000];
  struct sockaddr_in this_addr, server_addr, client_addr;
  struct hostent *host;
  int sin_size;

  // Contadores de tempo
  struct timeval ordering_time_begin, ordering_time_end;
  struct timeval client_con_time_begin[2], client_con_time_end[2];
  struct timeval server_con_time_begin, server_con_time_end;
  double ordering_time_spent, client_con_time_spent[2], server_con_time_spent;

  /* Checa argumentos da linha de comando
     argv[0] = Nome do aplicativo
     argv[1] = Porta do servidor que será usada pelos clientes
     argv[2] = Endereço do servidor no nível superior
     argv[3] = Porta do servidor no nível superior
  */
  if(argc < 4){
    printf("Uso : %s <porta client> <endereco server> <porta server> \n", argv[0]);
    exit(1);
  }

  // Cria socket
  if ((recsock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  {
    perror("Erro no Socket");
    exit(1);
  }

  if (setsockopt(recsock, SOL_SOCKET, SO_REUSEADDR, &true,sizeof(int)) == -1)
  {
    perror("Erro Setsockopt");
    exit(1);
  }


  // Configura o endereco deste servidor
  this_addr.sin_family = AF_INET;
  this_addr.sin_port = htons(atoi(argv[1]));
  this_addr.sin_addr.s_addr = INADDR_ANY;
  bzero(&(this_addr.sin_zero),8);


  // Informa endereço ao socket
  if (bind(recsock, (struct sockaddr *)&this_addr, sizeof(struct sockaddr)) == -1)
  {
    perror("Nao foi possivel realizar o bind");
    exit(1);
  }

  // Configura o socket para aguardar mensagens
  if (listen(recsock, 10) == -1)
  {
    perror("Erro de Listen");
    exit(1);
  }

  // Variavel para armazenar o tamanho de endereco do cliente conectado
  sin_size = sizeof(struct sockaddr_in);

  // Loop para receber os vetores dos dois clientes
  while(connections < 2 && (connected[connections] = accept(recsock, (struct sockaddr *)&client_addr, &sin_size)))
  {
    // Inicia contador de tempo de conexão com Clientes
    gettimeofday(&client_con_time_begin[connections], NULL);
    // Aguarda o recebimento do vetor
    recv(connected[connections], vectorin[connections], sizeof(int)*1250, 0);
    // Termina contador de tempo de conexão com Clientes
    gettimeofday(&client_con_time_end[connections], NULL);

    // Imprime vetor recebido
    printf("Vetor recebido: [ ");
    for(i = 0; i < 1250; i++)
    {
      printf("%d ", vectorin[connections][i]);
    }
    printf("]\n");

    connections++;
    sleep(1);
  }

  // Inicia contagem de tempo de ordenação do vetor
  gettimeofday(&ordering_time_begin, NULL);
  // Concatena e ordena os dois vetores
  concatena(vectorin[0], vectorin[1], vectorout);
  quickSort(vectorout, 0, 2499);
  // Termina contagem de tempo de ordenação do vetor
  gettimeofday(&ordering_time_end, NULL);

  // Inicia contador de tempo de conexão com o servidor nível 2
  gettimeofday(&server_con_time_begin, NULL);
  // Cria conexão com o servidor de nível 2
  host = gethostbyname(argv[2]);
   if ((sendsock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
   {
      perror("Erro de Socket");
      exit(1);
   }

   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons(atoi(argv[3]));
   server_addr.sin_addr = *((struct in_addr *)host->h_addr);
   bzero(&(server_addr.sin_zero),8);

  // Conecta com o servidor de nível 2
  if (connect(sendsock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
  {
    perror("Erro de conexao");
    exit(1);
  }
   
  // Envia vetor ordenado
  send(sendsock, vectorout, sizeof(int)*2500, 0);

  // Recebe vetor de resposta com 10000 elementos ordenados
  recv(sendsock, sortedvector, sizeof(int)*10000, 0);

  // Termina contagem do tempo de conexão com o servidor nível 2
  gettimeofday(&server_con_time_end, NULL);

  // Envia o vetor de resposta com 10000 elementos ordenados aos Clientes
  for(i = 0; i < connections; i++)
  {
    send(connected[i], sortedvector, sizeof(int)*10000, 0);
    close(connected[i]);
  }

  // Imprime vetor enviado
  printf("Vetor enviado: [ ");
  for(i = 0; i < 2500; i++)
  {
    printf("%d ", vectorout[i]);
  }
  printf("]\n");

  // Fecha os sockets dos clientes
  close(recsock);

  ordering_time_spent = ((ordering_time_end.tv_sec - ordering_time_begin.tv_sec)*10000000L + ordering_time_end.tv_usec) - ordering_time_begin.tv_usec;
  for (i = 0; i < connections; i++)
    client_con_time_spent[i] = ((client_con_time_end[i].tv_sec - client_con_time_begin[i].tv_sec)*10000000L + client_con_time_end[i].tv_usec) - client_con_time_begin[i].tv_usec;
  server_con_time_spent = ((server_con_time_end.tv_sec - server_con_time_begin.tv_sec)*10000000L + server_con_time_end.tv_usec) - server_con_time_begin.tv_usec;

  printf("Tempo de ordenação do vetor: %.2lf ms\n", ordering_time_spent / 10000);
  for (i = 0; i < connections; i++)
    printf("Tempo de conexão com o cliente %d: %.2lf ms\n", i, client_con_time_spent[i] / 10000);
  printf("Tempo de conexão com o servidor nível 2: %.2lf ms\n", server_con_time_spent / 10000);

  // Fecha o socket do servidor do nível 2
  close(sendsock);
  return 0;
}
